通过成员指针访问受保护的成员:这是黑客攻击吗?

Access to protected member through member-pointer: is it a hack?

我们都知道从基础 class 指定 protected 的成员只能从派生的 class 自己的实例访问。这是标准中的一项功能,已在 Stack Overflow 上多次讨论:

但似乎可以使用成员指针绕过此限制,因为用户 chtz has shown me:

struct Base { protected: int value; };
struct Derived : Base
{
    void f(Base const& other)
    {
        //int n = other.value; // error: 'int Base::value' is protected within this context
        int n = other.*(&Derived::value); // ok??? why?
        (void) n;
    }
};

Live demo on coliru

为什么这是可能的,它是一个想要的功能还是实施中某处的故障或标准的措辞?


从评论中出现了另一个问题:if Derived::f is called with an actual Base,这是未定义的行为吗?

基本上你所做的是欺骗编译器,这应该有效。我总是看到这类问题,有时人们会得到不好的结果,有时它会起作用,这取决于它如何转换为汇编代码。

我记得曾见过一个在整数上带有 const 关键字的案例,但后来通过一些技巧,这个人能够更改值并成功绕过编译器的感知。结果是:一个简单的数学运算的错误值。原因很简单:x86 中的汇编确实区分了常量和变量,因为某些指令在其操作码中确实包含常量。因此,由于编译器 认为 它是一个常量,它将把它视为一个常量并以优化的方式使用错误的 CPU 指令来处理它,baam,你结果数字有误。

换句话说:编译器将尝试执行它可以执行的所有规则,但您最终可能会欺骗它,并且根据您尝试执行的操作,您可能会或可能不会得到错误的结果,所以只有当你知道自己在做什么时,你才最好做这样的事情。

在您的例子中,指针 &Derived::value 可以根据从 class 开始的字节数从对象计算得出。这基本上就是编译器访问它的方式,因此,编译器:

  1. 没有发现任何权限问题,因为您正在 compile-time 通过 derived 访问 value
  2. 可以做到,因为您在与 derived 具有相同结构的对象中获取以字节为单位的偏移量(很明显,base)。

所以,你没有违反任何规则。您成功规避了编译规则。你不应该这样做,正是因为你所附链接中描述的原因,因为它破坏了 OOP 封装,但是,好吧,如果你知道你在做什么......

is it a hack?

与使用 reinterpret_cast 类似,这可能很危险,并且可能成为难以发现错误的潜在来源。但它的格式很好,毫无疑问它是否应该工作。

澄清类比:reinterpret_cast 的行为也在标准中明确指定,可以在没有任何 UB 的情况下使用。但是 reinterpret_cast 绕过了类型系统,类型系统的存在是有原因的。

[Is it] a glitch somewhere in the implementation or the wording of the Standard?

不,实现是正确的。这就是指定语言的工作方式。

Derived 的成员函数显然可以访问 &Derived::value,因为它是基类的受保护成员。

该操作的结果是指向 Base 成员的指针。这可以应用于对 Base 的引用。成员访问权限不适用于指向成员的指针:它仅适用于成员的名称。


From comments emerged another question: if Derived::f is called with an actual Base, is it undefined behaviour?

不是 UB。 Base有会员

无法使用 class 成员访问 expr.ref (aclass.amember) due to access control [class.access] 访问成员这一事实并不意味着无法使用其他表达式访问该成员。

表达式&Derived::value(whose type is int Base::*) is perfectly standard compliant, and it designates the member value of Base. Then the expression a_base.*p where p is a pointer to a member of Base and a_base an instance of Base is also standard compliant.

因此任何符合标准的编译器都应使表达式 other.*(&Derived::value); 定义行为:访问 other 的成员 value

只是为了增加答案并放大一点我可以从你的字里行间读到的恐怖。如果您将访问说明符视为 'the law',监管您以阻止您执行 'bad things',我认为您没有抓住要点。 publicprotectedprivateconst ... 都是系统的一部分,对 C++ 来说是一个巨大的优势。没有它的语言可能有很多优点,但当你构建大型系统时,这些东西是真正的资产。

话虽如此:我认为可以绕过几乎所有提供给您的安全网是一件好事。只要你记住 'possible' 并不意味着 'good'。这就是为什么它永远不应该是 'easy' 的原因。但对于其余的 - 这取决于你。你是建筑师。

几年前我可以简单地这样做(并且它可能在某些环境中仍然有效):

#define private public

对 'hostile' 外部头文件很有帮助。好的做法?你怎么看?但有时你的选择是有限的。

所以是的,你显示的是 kind-of 系统漏洞。但是,嘿,是什么让你无法推导并分发 public 对成员的引用?如果可怕的维护问题让您兴奋 - 无论如何,为什么不呢?